package com.mt.mtyx.common.utils.generate.context;


import com.mt.mtyx.common.utils.Helper;
import com.mt.mtyx.common.utils.generate.generate.config.DatabaseCodeConfig;
import com.mt.mtyx.common.utils.generate.generate.table.FieldData;
import com.mt.mtyx.common.utils.generate.generate.table.TableData;
import java.sql.*;
import java.util.*;


/**
 * @author WMT
 * @Date 2023/5/26 17:27
 */
public class DisplayTableData {

    private static Connection CONNECTION = null;
    private static PreparedStatement PRE = null;
    private static ResultSet RES = null;
    private static String DATASOURCE;

    public static Map<String, TableData> display(DatabaseCodeConfig config) {
        connection(config);
        getDataSource(config.getUrl());
        String table = "SELECT TABLE_NAME,TABLE_COMMENT,TABLE_SCHEMA FROM information_schema.TABLES";
        Map<String, TableData> tables = tables(generateSQL(table, config, 0));
        String field = "SELECT `TABLE_NAME`,`COLUMN_NAME`,`IS_NULLABLE`, `DATA_TYPE`,`COLUMN_COMMENT` FROM information_schema.columns";
        fields(generateSQL(field, config, 1), config.getFieldPrefixAlphabet(), tables);
        close();
        return tables;
    }

    private static String generateSQL(String sql, DatabaseCodeConfig config, int type) {
        StringBuilder sb = new StringBuilder(sql);
        sb.append(" WHERE TABLE_SCHEMA =  ").append("'").append(DATASOURCE).append("'").append(" ");
        if (Helper.isNotEmpty(config.getPrefixTable())) {
            sb.append("AND ").append("TABLE_NAME ").append("LIKE ").append("'").append(Helper.rightSql(config.getPrefixTable())).append("'").append(" ");
        }
        if (Helper.isNotEmpty(config.getSuffixTable())) {
            sb.append("AND ").append("TABLE_NAME ").append("LIKE ").append("'").append(Helper.leftSql(config.getSuffixTable())).append("'").append(" ");
        }
        if (config.getExcludeTable() != null && !config.getExcludeTable().isEmpty()) {
            sb.append("AND ").append("TABLE_NAME ").append("NOT IN ").append("(");
            for (String table : config.getExcludeTable()) {
                sb.append("'").append(table).append("'").append(",");
            }
            sb.deleteCharAt(sb.length() - 1);
            sb.append(") ");
        }
        if (config.getIncludeTable() != null && !config.getIncludeTable().isEmpty()) {
            sb.append("AND ").append("TABLE_NAME ").append("IN ").append("(");
            for (String table : config.getIncludeTable()) {
                sb.append("'").append(table).append("'").append(",");
            }
            sb.deleteCharAt(sb.length() - 1);
            sb.append(") ");
        }
        if (type == 1) sb.append(" ").append("ORDER BY ORDINAL_POSITION");
        return sb.toString();
    }

    private static void getDataSource(String url) {
        if (url == null) throw new IllegalArgumentException("url can`t is null");
        if (url.contains("?")) url = url.substring(0, url.indexOf("?"));
        StringBuilder database = new StringBuilder();
        for (int i = url.length() - 1; i >= 0; i--) {
            if (url.charAt(i) == '/') break;
            database.append(url.charAt(i));
        }
        DATASOURCE = database.reverse().toString();
    }

    private static void connection(DatabaseCodeConfig config) {
        try {
            Class.forName(config.getDriver());
            CONNECTION = DriverManager.getConnection(config.getUrl(), config.getUsername(), config.getPassword());
        } catch (SQLException | ClassNotFoundException sqlException) {
            sqlException.printStackTrace();
        }
    }

    private static Map<String, TableData> tables(String sql) {
        Map<String, TableData> map = new TreeMap<>();
        try {
            PRE = CONNECTION.prepareStatement(sql);
            RES = PRE.executeQuery();
            while (RES != null && RES.next()) {
                String table_name = RES.getString("TABLE_NAME");
                TableData data = new TableData(
                        table_name, table_name, RES.getString("TABLE_COMMENT"), RES.getString("TABLE_SCHEMA"),
                        new LinkedList<>()
                );
                map.put(table_name, data);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return map;
    }

    private static void fields(String sql, String prefix, Map<String, TableData> map) {
        try {
            Map<String, Class<?>> mapping = DatabaseCodeConfig.getTypeMap();
            PRE = CONNECTION.prepareStatement(sql);
            RES = PRE.executeQuery();
            while (RES != null && RES.next()) {
                String table_name = RES.getString("TABLE_NAME");
                String data_type = RES.getString("DATA_TYPE");
                String column_name = RES.getString("COLUMN_NAME");
                char[] chars = column_name.toCharArray();
                chars[0] = String.valueOf(chars[0]).toLowerCase().charAt(0);
                String propertyName = new String(chars);
                if (Helper.isNotEmpty(prefix) && column_name.startsWith(prefix)) {
                    for (int i = 0; i < prefix.length(); i++) {
                        chars[i] = String.valueOf(chars[i]).toLowerCase().charAt(0);
                    }
                    chars[prefix.length()] = String.valueOf(chars[prefix.length()]).toUpperCase(Locale.ROOT).charAt(0);
                } else {
                    chars[0] = String.valueOf(chars[0]).toUpperCase(Locale.ROOT).charAt(0);
                }
                String capitalName = new String(chars);
                if (map.containsKey(table_name)) {
                    List<FieldData> fields = map.get(table_name).getFields();
                    FieldData fieldData = new FieldData(
                            column_name,
                            column_name,
                            propertyName,
                            capitalName,
                            data_type, RES.getString("IS_NULLABLE"),
                            RES.getString("COLUMN_COMMENT"));
                    if (mapping.containsKey(data_type)) {
                        Class<?> clazz = mapping.get(data_type);
                        fieldData.setJavaType(clazz.getSimpleName());
                        fieldData.setJavaTypePackage(clazz.getName());
                    }
                    fields.add(fieldData);
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    private static void close() {
        try {
            if (CONNECTION != null) CONNECTION.close();
            if (PRE != null) PRE.close();
            if (RES != null) RES.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}
